/* This software was developed at the National Institute of Standards and
 * Technology by employees of the Federal Government in the course of
 * their official duties. Pursuant to title 17 Section 105 of the United
 * States Code this software is not subject to copyright protection and
 * is in the public domain.
 * NIST assumes no responsibility whatsoever for its use by other parties,
 * and makes no guarantees, expressed or implied, about its quality,
 * reliability, or any other characteristic.
 * <BR>
 * We would appreciate acknowledgement if the software is used.
 * <BR>
 * NIST ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
 * DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING
 * FROM THE USE OF THIS SOFTWARE.
 * </PRE></P>
 * @author  rouil
 */

#include "bsscheduler.h"
#include "burst.h"
#include "dlburst.h"
#include "ulburst.h"
#include "random.h"
#include "wimaxctrlagent.h"
#include "mac802_16BS.h"

/**
 * Bridge to TCL for BSScheduler
 */
static class BSSchedulerClass : public TclClass {
public:

    BSSchedulerClass() : TclClass("WimaxScheduler/BS") {
    }

    TclObject* create(int, const char*const*) {
        return (new BSScheduler());

    }
} class_bsscheduler;

/*
 * Create a scheduler
 */
BSScheduler::BSScheduler() {
    //debug2 ("BSScheduler created\n");
    default_mod_ = OFDM_BPSK_1_2;
    bind("dlratio_", &dlratio_);

    //dlratio_ = DEFAULT_DL_RATIO;

    contention_size_ = MIN_CONTENTION_SIZE;

    nextDL_ = -1;
    nextUL_ = -1;
    nextULNonData_ = -1;

    qos_record _tmp;
    _tmp.burst_mount = 0;
    _tmp.last_alloc_index = 0;
    _tmp.ratio = 1.0f;
    _tmp.used = 0.0f;
    _tmp.allocated = 0;
    qos_record_for_ul[0] = qos_record_for_ul[1] = qos_record_for_ul[2] = qos_record_for_ul[3] = qos_record_for_ul[4] = _tmp;
}

/*
 * Interface with the TCL script
 * @param argc The number of parameter
 * @param argv The list of parameters
 */
int BSScheduler::command(int argc, const char*const* argv) {
    if (argc == 3) {
        if (strcmp(argv[1], "set-default-modulation") == 0) {
            if (strcmp(argv[2], "OFDM_BPSK_1_2") == 0)
                default_mod_ = OFDM_BPSK_1_2;
            else if (strcmp(argv[2], "OFDM_QPSK_1_2") == 0)
                default_mod_ = OFDM_QPSK_1_2;
            else if (strcmp(argv[2], "OFDM_QPSK_3_4") == 0)
                default_mod_ = OFDM_QPSK_3_4;
            else if (strcmp(argv[2], "OFDM_16QAM_1_2") == 0)
                default_mod_ = OFDM_16QAM_1_2;
            else if (strcmp(argv[2], "OFDM_16QAM_3_4") == 0)
                default_mod_ = OFDM_16QAM_3_4;
            else if (strcmp(argv[2], "OFDM_64QAM_2_3") == 0)
                default_mod_ = OFDM_64QAM_2_3;
            else if (strcmp(argv[2], "OFDM_64QAM_3_4") == 0)
                default_mod_ = OFDM_64QAM_3_4;
            else
                return TCL_ERROR;
            return TCL_OK;
        } else if (strcmp(argv[1], "set-contention-size") == 0) {
            contention_size_ = atoi(argv[2]);
            assert(contention_size_ >= 0);
            return TCL_OK;
        } else if (strcmp(argv[1], "set-flow-ratio") == 0) {
            sscanf(argv[2], "%lf %lf %lf %lf %lf", &qos_record_for_ul[SERVICE_UGS].ratio, &qos_record_for_ul[SERVICE_ertPS].ratio,          \
                    &qos_record_for_ul[SERVICE_rtPS].ratio, &qos_record_for_ul[SERVICE_nrtPS].ratio, &qos_record_for_ul[SERVICE_BE].ratio);
            return TCL_OK;
        }

    }
    return TCL_ERROR;
}

/**
 * update the burst number with different qos type in ULMAP according to qos_record_for_ul array.
 */
void BSScheduler::update_burst() {
    OFDMPhy *phy = mac_->getPhy();

    //checking and setting variable for scheduling
    int nbPS = (int) floor((mac_->getFrameDuration() / phy->getPS()));
    int nbPS_left = nbPS - mac_->phymib_.rtg - mac_->phymib_.ttg;
    int nbSymbols = (int) floor((phy->getPS() * nbPS_left) / phy->getSymbolTime());
    int maxdlduration = (int) (nbSymbols / (1.0 / dlratio_)); //number of symbols for downlink
    int maxulduration = nbSymbols - maxdlduration; //number of symbols for uplink

    int rangingduration = (int) ceil(((phy->getPS() * contention_size_ * getInitRangingopportunity()) / phy->getSymbolTime()));
    rangingduration++;

    int bwduration = (int) ceil(((phy->getPS() * contention_size_ * getBWopportunity()) / phy->getSymbolTime()));
    bwduration++;

    int remaining = maxulduration - rangingduration - bwduration;
    double denom = qos_record_for_ul[SERVICE_UGS].ratio + qos_record_for_ul[SERVICE_ertPS].ratio +          \
                   qos_record_for_ul[SERVICE_rtPS].ratio + qos_record_for_ul[SERVICE_nrtPS].ratio + qos_record_for_ul[SERVICE_BE].ratio;
    qos_record_for_ul[SERVICE_UGS].burst_mount = (int) ((double) (qos_record_for_ul[SERVICE_UGS].ratio * remaining + 0.5) / denom);
    qos_record_for_ul[SERVICE_ertPS].burst_mount = (int) ((double) (qos_record_for_ul[SERVICE_ertPS].ratio * remaining + 0.5) / denom);
    qos_record_for_ul[SERVICE_rtPS].burst_mount = (int) ((double) (qos_record_for_ul[SERVICE_rtPS].ratio * remaining + 0.5) / denom);
    qos_record_for_ul[SERVICE_nrtPS].burst_mount = (int) ((double) (qos_record_for_ul[SERVICE_nrtPS].ratio * remaining + 0.5) / denom);
    qos_record_for_ul[SERVICE_BE].burst_mount = (int) ((double) (qos_record_for_ul[SERVICE_BE].ratio * remaining + 0.5) / denom);
    qos_record_for_ul[SERVICE_BE].burst_mount += remaining - qos_record_for_ul[SERVICE_UGS].burst_mount - qos_record_for_ul[SERVICE_ertPS].burst_mount -          \
            qos_record_for_ul[SERVICE_rtPS].burst_mount - qos_record_for_ul[SERVICE_nrtPS].burst_mount - qos_record_for_ul[SERVICE_BE].burst_mount;

    printf("\t total burst:%5d\n", maxulduration);
    printf("\t     ranging:%5d\n", rangingduration);
    printf("\tBWcontention:%5d\n", bwduration);
    printf("\t         UGS:%5d\n", qos_record_for_ul[SERVICE_UGS].burst_mount);
    printf("\t       ertPS:%5d\n", qos_record_for_ul[SERVICE_ertPS].burst_mount);
    printf("\t        rtPS:%5d\n", qos_record_for_ul[SERVICE_rtPS].burst_mount);
    printf("\t       nrtPS:%5d\n", qos_record_for_ul[SERVICE_nrtPS].burst_mount);
    printf("\t          BE:%5d\n", qos_record_for_ul[SERVICE_BE].burst_mount);
}

/**
 * Initializes the scheduler
 */
void BSScheduler::init() {
    WimaxScheduler::init();

    // If the user did not set the profiles by hand, let's do it
    // automatically
    if (getMac()->getMap()->getDlSubframe()->getProfile(DIUC_PROFILE_1) == NULL) {
        Profile *p = getMac()->getMap()->getDlSubframe()->addProfile((int) round((getMac()->getPhy()->getFreq() / 1000)), OFDM_BPSK_1_2);
        p->setIUC(DIUC_PROFILE_1);
        p = getMac()->getMap()->getDlSubframe()->addProfile((int) round((getMac()->getPhy()->getFreq() / 1000)), OFDM_QPSK_1_2);
        p->setIUC(DIUC_PROFILE_2);
        p = getMac()->getMap()->getDlSubframe()->addProfile((int) round((getMac()->getPhy()->getFreq() / 1000)), OFDM_QPSK_3_4);
        p->setIUC(DIUC_PROFILE_3);
        p = getMac()->getMap()->getDlSubframe()->addProfile((int) round((getMac()->getPhy()->getFreq() / 1000)), OFDM_16QAM_1_2);
        p->setIUC(DIUC_PROFILE_4);
        p = getMac()->getMap()->getDlSubframe()->addProfile((int) round((getMac()->getPhy()->getFreq() / 1000)), OFDM_16QAM_3_4);
        p->setIUC(DIUC_PROFILE_5);
        p = getMac()->getMap()->getDlSubframe()->addProfile((int) round((getMac()->getPhy()->getFreq() / 1000)), OFDM_64QAM_2_3);
        p->setIUC(DIUC_PROFILE_6);
        p = getMac()->getMap()->getDlSubframe()->addProfile((int) round((getMac()->getPhy()->getFreq() / 1000)), OFDM_64QAM_3_4);
        p->setIUC(DIUC_PROFILE_7);

        p = getMac()->getMap()->getUlSubframe()->addProfile(0, default_mod_);
        p->setIUC(UIUC_INITIAL_RANGING);
        p = getMac()->getMap()->getUlSubframe()->addProfile(0, default_mod_);
        p->setIUC(UIUC_REQ_REGION_FULL);
        p = getMac()->getMap()->getUlSubframe()->addProfile(0, default_mod_);
        p->setIUC(UIUC_REQ_REGION_FOCUSED);

        p = getMac()->getMap()->getUlSubframe()->addProfile(0, OFDM_BPSK_1_2);
        p->setIUC(UIUC_PROFILE_1);
        p = getMac()->getMap()->getUlSubframe()->addProfile(0, OFDM_QPSK_1_2);
        p->setIUC(UIUC_PROFILE_2);
        p = getMac()->getMap()->getUlSubframe()->addProfile(0, OFDM_QPSK_3_4);
        p->setIUC(UIUC_PROFILE_3);
        p = getMac()->getMap()->getUlSubframe()->addProfile(0, OFDM_16QAM_1_2);
        p->setIUC(UIUC_PROFILE_4);
        p = getMac()->getMap()->getUlSubframe()->addProfile(0, OFDM_16QAM_3_4);
        p->setIUC(UIUC_PROFILE_5);
        p = getMac()->getMap()->getUlSubframe()->addProfile(0, OFDM_64QAM_2_3);
        p->setIUC(UIUC_PROFILE_6);
        p = getMac()->getMap()->getUlSubframe()->addProfile(0, OFDM_64QAM_3_4);
        p->setIUC(UIUC_PROFILE_7);
    }

    //init contention slots
    ContentionSlot *slot = getMac()->getMap()->getUlSubframe()->getRanging();
    slot->setSize(getInitRangingopportunity());
    slot->setBackoff_start(getMac()->macmib_.rng_backoff_start);
    slot->setBackoff_stop(getMac()->macmib_.rng_backoff_stop);

    slot = getMac()->getMap()->getUlSubframe()->getBw_req();
    slot->setSize(getBWopportunity());
    slot->setBackoff_start(getMac()->macmib_.bw_backoff_start);
    slot->setBackoff_stop(getMac()->macmib_.rng_backoff_stop);

    update_burst();
}

/**
 * Compute and return the bandwidth request opportunity size
 * @return The bandwidth request opportunity size
 */
int BSScheduler::getBWopportunity() {
    int nbPS = BW_REQ_PREAMBLE * getMac()->getPhy()->getSymbolPS();
    //add PS for carrying header
    nbPS += (int) round((getMac()->getPhy()->getTrxTime(HDR_MAC802_16_SIZE, getMac()->getMap()->getUlSubframe()->getProfile(UIUC_REQ_REGION_FULL)->getEncoding()) / getMac()->getPhy()->getPS()));
    //printf ("BWopportunity size=%d\n", nbPS);
    return nbPS;
}

/**
 * Compute and return the initial ranging opportunity size
 * @return The initial ranging opportunity size
 */
int BSScheduler::getInitRangingopportunity() {
    int nbPS = INIT_RNG_PREAMBLE * getMac()->getPhy()->getSymbolPS();
    //add PS for carrying header
    nbPS += (int) round((getMac()->getPhy()->getTrxTime(RNG_REQ_SIZE + HDR_MAC802_16_SIZE, getMac()->getMap()->getUlSubframe()->getProfile(UIUC_INITIAL_RANGING)->getEncoding()) / getMac()->getPhy()->getPS()));
    //printf ("Init ranging opportunity size=%d\n", nbPS);
    return nbPS;
}

/**
 * Schedule bursts/packets
 */
void BSScheduler::schedule() {
    //The scheduler will perform the following steps:
    //1-Clear DL map
    //2-Allocate Burst for Broadcast message
    //3-Pick Unicast bursts for Downlink (Best Effort) in a RR fashion
    //4-Clear UL map
    //5-Allocate contention slots for initial ranging
    //6-Allocate contention slots for bandwidth requests
    //7-Allocate unicast burst for Uplink

    double txtime; //tx time for some data (in second)
    int txtime_s; //number of symbols used to transmit the data
    UlBurst *ub;
    PeerNode *peer;

    OFDMPhy *phy = mac_->getPhy();
    FrameMap *map = mac_->getMap();

    //checking and setting variable for scheduling
    int nbPS = (int) floor((mac_->getFrameDuration() / phy->getPS()));
    assert(nbPS * phy->getPS() <= mac_->getFrameDuration()); //check for rounding errors
    int nbPS_left = nbPS - mac_->phymib_.rtg - mac_->phymib_.ttg;
    int nbSymbols = (int) floor((phy->getPS() * nbPS_left) / phy->getSymbolTime());
    assert(nbSymbols * phy->getSymbolTime()+(mac_->phymib_.rtg + mac_->phymib_.ttg) * phy->getPS() < mac_->getFrameDuration());
    int maxdlduration = (int) (nbSymbols / (1.0 / dlratio_)); //number of symbols for downlink
    int maxulduration = nbSymbols - maxdlduration; //number of symbols for uplink
    int ulduration = 0; //number of symbols currently used for uplink
    int nbulpdus = 0;

    assert((nbSymbols * phy->getSymbolPS() + mac_->phymib_.rtg + mac_->phymib_.ttg) * phy->getPS() < mac_->getFrameDuration());
    assert(maxdlduration * phy->getSymbolTime() + mac_->phymib_.rtg * phy->getPS() + maxulduration * phy->getSymbolTime() + mac_->phymib_.ttg * phy->getPS() < mac_->getFrameDuration());

    debug2("Frame: duration=%f, PSduration=%e, symboltime=%e, nbPS=%d, rtg=%d, ttg=%d, PSleft=%d, nbSymbols=%d, ",                      \
	  mac_->getFrameDuration(), phy->getPS(), phy->getSymbolTime(), nbPS, mac_->phymib_.rtg, mac_->phymib_.ttg, nbPS_left, nbSymbols);
    debug2("maxdlduration=%d, maxulduration=%d\n", maxdlduration, maxulduration);
    //printf ("%d %9f\n",phy->getSymbolPS(), (nbSymbols*phy->getSymbolPS()+mac_->phymib_.rtg + mac_->phymib_.ttg)*phy->getPS());

    //1-Clear DL map
    //2-Allocate Burst for Broadcast message
    //3-Pick Unicast bursts for Downlink (Best Effort) in a RR fashion
    map->getDlSubframe()->getPdu()->removeAllBursts();
    alloc_dlmap(maxdlduration);

    //Start the Uplink allocation
    //4-Set start time for uplink subframe and clean previous allocation
    map->getUlSubframe()->setStarttime(maxdlduration * phy->getSymbolPS() + mac_->phymib_.rtg);
    while (map->getUlSubframe()->getNbPdu() > 0) {
        PhyPdu *pdu = map->getUlSubframe()->getPhyPdu(0);
        pdu->removeAllBursts();
        map->getUlSubframe()->removePhyPdu(pdu);
        delete (pdu);
    }

    //5-Set contention slots for initial ranging
    int rangingduration = (int) ceil(((phy->getPS() * contention_size_ * getInitRangingopportunity()) / phy->getSymbolTime()));
    assert(rangingduration < maxulduration - ulduration); //check there is enough space
    ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
    ub->setIUC(UIUC_INITIAL_RANGING);
    ub->setDuration(rangingduration);
    ub->setStarttime(ulduration); //we put the contention at the begining
    ulduration += ub->getDuration() + 1;

    //6-Set contention slots for bandwidth request
    int bwduration = (int) ceil(((phy->getPS() * contention_size_ * getBWopportunity()) / phy->getSymbolTime()));
    assert(bwduration < maxulduration - ulduration);
    ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
    ub->setIUC(UIUC_REQ_REGION_FULL);
    ub->setDuration(bwduration);
    ub->setStarttime(ulduration); //we put the contention at the begining
    ulduration += ub->getDuration() + 1;

    //check if there is Fast Ranging allocation to do
    FastRangingInfo *next_info = NULL;
    for (FastRangingInfo *info = getMac()->fast_ranging_head_.lh_first; info; info = next_info) {
        //get next info before the entry is removed from list
        next_info = info->next_entry();
        if (info->frame() == mac_->getFrameNumber()) {
            //we need to include a fast ranging allocation
            ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
            int tmp = (int) ceil(((phy->getPS() * getInitRangingopportunity()) / phy->getSymbolTime()));
            ub->setIUC(UIUC_EXT_UIUC);
            ub->setDuration(tmp);
            ub->setStarttime(ulduration); //start after previous slot
            ub->setFastRangingParam(info->macAddr(), UIUC_INITIAL_RANGING);
            ulduration += ub->getDuration() + 1;
            mac_->debug("At %f in Mac %d adding fast ranging for %d\n", NOW, mac_->addr(), info->macAddr());
            info->remove_entry();
        }
    }

    //7-Allocate unicast burst for Uplink
    //7.1-Allocate BASIC,PRIMARY,SECONDARY connection first(in RR-fashion)
    peer = mac_->getPeerNode(nextULNonData_);
    if (peer == NULL) //if the node is not here, lets start from the beginning
        peer = mac_->getPeerNode_head();
    if (peer) {
        for (int i = 0; i < mac_->getNbPeerNodes() && ulduration < maxulduration; i++) {
            //The following code uses BW information to allocate data
            if (peer->getReqManagementBw() > 0 && !getMac()->isPeerScanning(peer->getAddr())) {
                //fprintf(stdout,"At %f Allocating UL burst for Node %d\n", NOW, peer->getAddr());
                //add a pdu and burst for this node
                ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
                ub->setCid(peer->getBasic(IN_CONNECTION)->get_cid()); //use BASIC cid for management connection
                //get the profile (we do a simple translation so that DL_PROFILE_1 = UL_PROFILE_1
                ub->setIUC(map->getUlSubframe()->getProfile(peer->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getIUC());
                ub->setStarttime(ulduration);

                txtime = phy->getTrxSymbolTime(peer->getReqManagementBw(), map->getUlSubframe()->getProfile(ub->getIUC())->getEncoding());
                txtime_s = (int) ceil(txtime / phy->getSymbolTime() - 0.000001f); //in units of symbol
                if (txtime_s <= maxulduration - ulduration) {
                    ub->setDuration(txtime_s);
                    ulduration += ub->getDuration();
                } else {
                    //fill up the rest
                    ub->setDuration(maxulduration - ulduration);
                    ulduration = maxulduration;
                }
                mac_->debug("duration= %d\n", ub->getDuration());
            }

            peer = peer->next_entry();
            if (peer == NULL)
                peer = mac_->getPeerNode_head();

            if (peer)
                nextULNonData_ = peer->getAddr(); //go to next one
            else
                nextULNonData_ = -1; //will go the the beginning of the list
        }
    }

    unsigned int non_empty_flow;
    int avg_burst_per_flow;
    int burstsize_in_byte;

    //7.2-insert requests bw for the periodic grants of UGS,ertPS,rtPS,nrtPS
    //===========================UGS start==================================
    vector<Connection *> UGSList = getflow(SERVICE_UGS);
    non_empty_flow = 0;
    for (unsigned i = 0; i < UGSList.size(); i++) {
        if (NOW - UGSList[i]->getLastGrantTime() > UGSList[i]->get_serviceflow()->getQoS()->getInterval()) {
            UGSList[i]->setBw(UGSList[i]->get_serviceflow()->getQoS()->getBurstSize());
            UGSList[i]->setLastGrantTime(NOW);
        }
        if (UGSList[i]->getBw() > 0) {
            non_empty_flow++;
        }
        UGSList[i]->setUlDuration(0);
    }

    qos_record_for_ul[SERVICE_UGS].allocated = 0;
    if (non_empty_flow != 0) {
        burstsize_in_byte = (int) phy->getMaxPktSize(1.0f, map->getUlSubframe()->getProfile(UGSList[0]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getEncoding());
        //printf("burstsize_in_byte = %d\n",burstsize_in_byte);
        avg_burst_per_flow = (int) (qos_record_for_ul[SERVICE_UGS].burst_mount / non_empty_flow + 0.000001f);
        for (unsigned i = 0; i < UGSList.size(); i++) {
            txtime = phy->getTrxSymbolTime(UGSList[i]->getBw(), map->getUlSubframe()->getProfile(UGSList[i]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getEncoding());
            txtime_s = (int) ceil(txtime / phy->getSymbolTime() - 0.000001f);
            if (txtime_s <= avg_burst_per_flow) {
                UGSList[i]->setUlDuration(txtime_s);
                qos_record_for_ul[SERVICE_UGS].allocated += txtime_s;
                UGSList[i]->setBw(0);
            } else {
                UGSList[i]->setUlDuration(avg_burst_per_flow);
                qos_record_for_ul[SERVICE_UGS].allocated += avg_burst_per_flow;
                UGSList[i]->setBw(UGSList[i]->getBw() - avg_burst_per_flow * burstsize_in_byte);
                assert(UGSList[i]->getBw() > 0);
            }
        }
        for (unsigned int i = 0; i < UGSList.size() && qos_record_for_ul[SERVICE_UGS].allocated < qos_record_for_ul[SERVICE_UGS].burst_mount; i++) {
            int j = qos_record_for_ul[SERVICE_UGS].last_alloc_index;
            if (UGSList[qos_record_for_ul[SERVICE_UGS].last_alloc_index]->getBw() != 0 &&         \
                    qos_record_for_ul[SERVICE_UGS].allocated < qos_record_for_ul[SERVICE_UGS].burst_mount) {
                txtime = phy->getTrxSymbolTime(UGSList[j]->getBw(), map->getUlSubframe()->getProfile(UGSList[j]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getEncoding());
                txtime_s = (int) ceil(txtime / phy->getSymbolTime() - 0.000001f);
                if ((txtime_s + qos_record_for_ul[SERVICE_UGS].allocated) <= qos_record_for_ul[SERVICE_UGS].burst_mount) {
                    UGSList[j]->setUlDuration(UGSList[j]->getUlDuration() + txtime_s);
                    qos_record_for_ul[SERVICE_UGS].allocated += txtime_s;
                    UGSList[j]->setBw(0);
                } else {
                    UGSList[j]->setUlDuration(UGSList[j]->getUlDuration() + qos_record_for_ul[SERVICE_UGS].burst_mount - qos_record_for_ul[SERVICE_UGS].allocated);
                    UGSList[j]->setBw(UGSList[j]->getBw() - (qos_record_for_ul[SERVICE_UGS].burst_mount - qos_record_for_ul[SERVICE_UGS].allocated) * burstsize_in_byte);
                    qos_record_for_ul[SERVICE_UGS].allocated = qos_record_for_ul[SERVICE_UGS].burst_mount;
                    assert(UGSList[j]->getBw() > 0);
                }
            }
            qos_record_for_ul[SERVICE_UGS].last_alloc_index++;
            qos_record_for_ul[SERVICE_UGS].last_alloc_index %= (int) UGSList.size();
        }
        int alloc_test = 0;
        for (unsigned int i = 0; i < UGSList.size(); i++) {
            alloc_test += (int) UGSList[i]->getUlDuration();
        }
        assert(alloc_test <= qos_record_for_ul[SERVICE_UGS].burst_mount);
        assert(alloc_test == qos_record_for_ul[SERVICE_UGS].allocated);

        for (unsigned int i = 0; i < UGSList.size() && ulduration < maxulduration; i++) {
            if (UGSList[i]->getUlDuration() != 0) {
                ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
                ub->setCid(UGSList[i]->get_cid()); //specific cid(type = CONN_DATA)
                ub->setIUC(map->getUlSubframe()->getProfile(UGSList[i]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getIUC());
                ub->setStarttime(ulduration);
                if (UGSList[i]->getUlDuration() <= maxulduration - ulduration) {
                    ub->setDuration(UGSList[i]->getUlDuration());
                    ulduration += ub->getDuration();
                } else {
                    ub->setDuration(maxulduration - ulduration);
                    ulduration = maxulduration;
                }
            }
        }
    }

    //===========================ertPS start==================================
    vector<Connection *> ertPSList = getflow(SERVICE_ertPS);
    non_empty_flow = 0;
    for (unsigned i = 0; i < ertPSList.size(); i++) {
        if (NOW - ertPSList[i]->getLastGrantTime() > ertPSList[i]->get_serviceflow()->getQoS()->getInterval()) {
            if (ertPSList[i]->get_serviceflow()->getQoS()->getBurstSize() > ertPSList[i]->get_serviceflow()->getQoS()->getMinimumBandwidth()) {
                if (ertPSList[i]->get_serviceflow()->getQoS()->getBurstSize() > ertPSList[i]->getBw())
                    ertPSList[i]->setBw(ertPSList[i]->get_serviceflow()->getQoS()->getBurstSize());
            } else {
                if (ertPSList[i]->get_serviceflow()->getQoS()->getMinimumBandwidth() > ertPSList[i]->getBw())
                    ertPSList[i]->setBw(ertPSList[i]->get_serviceflow()->getQoS()->getMinimumBandwidth());
            }
            ertPSList[i]->get_serviceflow()->getQoS()->setBurstSize(0);
            ertPSList[i]->setLastGrantTime(NOW);
        }
        if (ertPSList[i]->getBw() > 0) {
            non_empty_flow++;
        }
        ertPSList[i]->setUlDuration(0);
    }

    qos_record_for_ul[SERVICE_ertPS].allocated = 0;
    if (non_empty_flow != 0) {
        burstsize_in_byte = (int) phy->getMaxPktSize((double) 1, map->getUlSubframe()->getProfile(ertPSList[0]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getEncoding());
        //printf("burstsize_in_byte = %d\n",burstsize_in_byte);
        avg_burst_per_flow = (int) (qos_record_for_ul[SERVICE_ertPS].burst_mount / non_empty_flow + 0.000001f);
        for (unsigned i = 0; i < ertPSList.size(); i++) {
            txtime = phy->getTrxSymbolTime(ertPSList[i]->getBw(), map->getUlSubframe()->getProfile(ertPSList[i]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getEncoding());
            txtime_s = (int) ceil(txtime / phy->getSymbolTime() - 0.000001f);
            if (txtime_s <= avg_burst_per_flow) {
                ertPSList[i]->setUlDuration(txtime_s);
                qos_record_for_ul[SERVICE_ertPS].allocated += txtime_s;
                ertPSList[i]->setBw(0);
            } else {
                ertPSList[i]->setUlDuration(avg_burst_per_flow);
                qos_record_for_ul[SERVICE_ertPS].allocated += avg_burst_per_flow;
                ertPSList[i]->setBw(ertPSList[i]->getBw() - avg_burst_per_flow * burstsize_in_byte);
                assert(ertPSList[i]->getBw() > 0);
            }
        }
        for (unsigned int i = 0; i < ertPSList.size() && qos_record_for_ul[SERVICE_ertPS].allocated < qos_record_for_ul[SERVICE_ertPS].burst_mount; i++) {
            int j = qos_record_for_ul[SERVICE_ertPS].last_alloc_index;
            if (ertPSList[j]->getBw() != 0 &&         \
                    qos_record_for_ul[SERVICE_ertPS].allocated < qos_record_for_ul[SERVICE_ertPS].burst_mount) {
                //printf("ertPSList[j]->getBw() %d\n",ertPSList[j]->getBw());
                txtime = phy->getTrxSymbolTime(ertPSList[j]->getBw(), map->getUlSubframe()->getProfile(ertPSList[j]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getEncoding());
                //printf("txtime %lf\n",txtime);
                //printf("phy->getSymbolTime() %lf\n",phy->getSymbolTime());
                //printf("txtime / phy->getSymbolTime() %lf\n",txtime / phy->getSymbolTime());
                txtime_s = (int) ceil(txtime / phy->getSymbolTime() - 0.000001f);
                //printf("txtime_s %d\n",txtime_s);
                //printf("qos_record_for_ul[SERVICE_ertPS].allocated %d\n",qos_record_for_ul[SERVICE_ertPS].allocated);
                //printf("qos_record_for_ul[SERVICE_ertPS].burst_mount %d\n",qos_record_for_ul[SERVICE_ertPS].burst_mount);
                if ((txtime_s + qos_record_for_ul[SERVICE_ertPS].allocated) <= qos_record_for_ul[SERVICE_ertPS].burst_mount) {
                    ertPSList[j]->setUlDuration(ertPSList[j]->getUlDuration() + txtime_s);
                    qos_record_for_ul[SERVICE_ertPS].allocated += txtime_s;
                    ertPSList[j]->setBw(0);
                } else {
                    ertPSList[j]->setUlDuration(ertPSList[j]->getUlDuration() + qos_record_for_ul[SERVICE_ertPS].burst_mount - qos_record_for_ul[SERVICE_ertPS].allocated);
                    ertPSList[j]->setBw(ertPSList[j]->getBw() - (qos_record_for_ul[SERVICE_ertPS].burst_mount - qos_record_for_ul[SERVICE_ertPS].allocated) * burstsize_in_byte);
                    qos_record_for_ul[SERVICE_ertPS].allocated = qos_record_for_ul[SERVICE_ertPS].burst_mount;
                    assert(ertPSList[j]->getBw() > 0);
                }
            }
            qos_record_for_ul[SERVICE_ertPS].last_alloc_index++;
            qos_record_for_ul[SERVICE_ertPS].last_alloc_index %= (int) ertPSList.size();
        }
        int alloc_test = 0;
        for (unsigned int i = 0; i < ertPSList.size(); i++) {
            alloc_test += (int) ertPSList[i]->getUlDuration();
        }
        assert(alloc_test <= qos_record_for_ul[SERVICE_ertPS].burst_mount);
        assert(alloc_test == qos_record_for_ul[SERVICE_ertPS].allocated);

        for (unsigned int i = 0; i < ertPSList.size() && ulduration < maxulduration; i++) {
            if (ertPSList[i]->getUlDuration() != 0) {
                ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
                ub->setCid(ertPSList[i]->get_cid()); //specific cid(type = CONN_DATA)
                ub->setIUC(map->getUlSubframe()->getProfile(ertPSList[i]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getIUC());
                ub->setStarttime(ulduration);
                if (ertPSList[i]->getUlDuration() <= maxulduration - ulduration) {
                    ub->setDuration(ertPSList[i]->getUlDuration());
                    ulduration += ub->getDuration();
                } else {
                    ub->setDuration(maxulduration - ulduration);
                    ulduration = maxulduration;
                }
            }
        }
    }
    //    for (unsigned i = 0; i < ertPSList.size() && ulduration < maxulduration; i++) {
    //        if (NOW - ertPSList[i]->getLastGrantTime() > ertPSList[i]->get_serviceflow()->getQoS()->getInterval()) {
    //            ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
    //            ub->setCid(ertPSList[i]->get_cid()); //specific cid(type = CONN_DATA)
    //            ub->setIUC(map->getUlSubframe()->getProfile(peer->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getIUC());
    //            ub->setStarttime(ulduration);
    //
    //            //@todo: Set the minimum bandwidth to 321, need rewrite as a variable
    //            if (ertPSList[i]->getBw() < 321)
    //                txtime = phy->getTrxSymbolTime(321, map->getUlSubframe()->getProfile(ub->getIUC())->getEncoding());
    //            else {
    //                txtime = phy->getTrxSymbolTime(ertPSList[i]->getBw(), map->getUlSubframe()->getProfile(ub->getIUC())->getEncoding());
    //                //@todo: remove debug message by ix
    //                //printf("bandwidth accepted = %d\n", ertPSList[i]->getBw());
    //            }
    //            //txtime = phy->getTrxSymbolTime(ertPSList[i]->get_serviceflow()->getQoS()->getBurstSize(), map->getUlSubframe()->getProfile(ub->getIUC())->getEncoding());
    //            txtime_s = (int) ceil(txtime / phy->getSymbolTime());
    //            //printf("    cid is %d, bw request = %d, txttime = %f, symboltime = %f\n", ertPSList[i]->get_cid(), ertPSList[i]->getBw(), txtime, phy->getSymbolTime());
    //            //printf("        granted burst = %d\n", txtime_s);
    //            if (txtime_s <= maxulduration - ulduration) {
    //                ub->setDuration(txtime_s);
    //                ulduration += ub->getDuration(); //add 1 OFDM symbol between bursts
    //            } else {
    //                //fill up the rest
    //                ub->setDuration(maxulduration - ulduration);
    //                ulduration = maxulduration;
    //            }
    //            //printf("duration= %d\n", ub->getDuration());
    //            ertPSList[i]->setLastGrantTime(NOW);
    //        }
    //    }

    //===========================rtPS,nrtPS start==================================
    qos_record_for_ul[SERVICE_rtPS].allocated = 0;
    vector<Connection *> rtPSList = getflow(SERVICE_rtPS);
    //allocate burst for unicast polling
    for (unsigned i = 0; i < rtPSList.size() && ulduration < maxulduration &&      \
            qos_record_for_ul[SERVICE_rtPS].allocated < qos_record_for_ul[SERVICE_rtPS].burst_mount; i++) {
        if (((rtPSList[i]->last_req_grant_+1) % rtPSList[i]->get_serviceflow()->getQoS()->getRequestInterval()) == 0) {
//            ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
//            ub->setCid(rtPSList[i]->get_cid());
//            ub->setIUC(UIUC_REQ_REGION_FOCUSED); //UIUC for unicast polling
//            ub->setStarttime(ulduration);
//            txtime_s = 1;
//            ub->setDuration(txtime_s);
//            ulduration += ub->getDuration();
            //printf("%d reserved\n",i);
            qos_record_for_ul[SERVICE_rtPS].allocated++;
        }
    }
    non_empty_flow = 0;
    for (unsigned i = 0; i < rtPSList.size(); i++) {
        if (rtPSList[i]->getBw() > 0) {
            non_empty_flow++;
        }
        rtPSList[i]->setUlDuration(0);
    }

    //qos_record_for_ul[SERVICE_rtPS].allocated = 0;
    if (non_empty_flow != 0) {
        burstsize_in_byte = (int) phy->getMaxPktSize(1.0f, map->getUlSubframe()->getProfile(rtPSList[0]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getEncoding());
        //printf("burstsize_in_byte = %d\n",burstsize_in_byte);
        avg_burst_per_flow = (int) ((qos_record_for_ul[SERVICE_rtPS].burst_mount - qos_record_for_ul[SERVICE_rtPS].allocated) / non_empty_flow + 0.000001f);
        for (unsigned i = 0; i < rtPSList.size(); i++) {
            txtime = phy->getTrxSymbolTime(rtPSList[i]->getBw(), map->getUlSubframe()->getProfile(rtPSList[i]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getEncoding());
            txtime_s = (int) ceil(txtime / phy->getSymbolTime() - 0.000001f);
            if (txtime_s <= avg_burst_per_flow) {
                rtPSList[i]->setUlDuration(txtime_s);
                qos_record_for_ul[SERVICE_rtPS].allocated += txtime_s;
                rtPSList[i]->setBw(0);
            } else {
                rtPSList[i]->setUlDuration(avg_burst_per_flow);
                qos_record_for_ul[SERVICE_rtPS].allocated += avg_burst_per_flow;
                rtPSList[i]->setBw(rtPSList[i]->getBw() - avg_burst_per_flow * burstsize_in_byte);
                assert(rtPSList[i]->getBw() >= 0);
            }
        }
        assert(qos_record_for_ul[SERVICE_rtPS].allocated <= qos_record_for_ul[SERVICE_rtPS].burst_mount);
        for (unsigned int i = 0; i < rtPSList.size() && qos_record_for_ul[SERVICE_rtPS].allocated < qos_record_for_ul[SERVICE_rtPS].burst_mount; i++) {
            int j = qos_record_for_ul[SERVICE_rtPS].last_alloc_index;
            if (rtPSList[j]->getBw() != 0 &&         \
                    qos_record_for_ul[SERVICE_rtPS].allocated < qos_record_for_ul[SERVICE_rtPS].burst_mount) {
                txtime = phy->getTrxSymbolTime(rtPSList[j]->getBw(), map->getUlSubframe()->getProfile(rtPSList[j]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getEncoding());
                txtime_s = (int) ceil(txtime / phy->getSymbolTime() - 0.000001f);
                if ((txtime_s + qos_record_for_ul[SERVICE_rtPS].allocated) <= qos_record_for_ul[SERVICE_rtPS].burst_mount) {
                    rtPSList[j]->setUlDuration(rtPSList[j]->getUlDuration() + txtime_s);
                    qos_record_for_ul[SERVICE_rtPS].allocated += txtime_s;
                    rtPSList[j]->setBw(0);
                } else {
                    rtPSList[j]->setUlDuration(rtPSList[j]->getUlDuration() + qos_record_for_ul[SERVICE_rtPS].burst_mount - qos_record_for_ul[SERVICE_rtPS].allocated);
                    rtPSList[j]->setBw(rtPSList[j]->getBw() - (qos_record_for_ul[SERVICE_rtPS].burst_mount - qos_record_for_ul[SERVICE_rtPS].allocated) * burstsize_in_byte);
                    qos_record_for_ul[SERVICE_rtPS].allocated = qos_record_for_ul[SERVICE_rtPS].burst_mount;
                    assert(rtPSList[j]->getBw() > 0);
                }
            }
            qos_record_for_ul[SERVICE_rtPS].last_alloc_index++;
            qos_record_for_ul[SERVICE_rtPS].last_alloc_index %= (int) rtPSList.size();
        }
        int alloc_test = 0;
        for (unsigned int i = 0; i < rtPSList.size(); i++) {
            alloc_test += (int) rtPSList[i]->getUlDuration();
        }
        assert(alloc_test <= qos_record_for_ul[SERVICE_rtPS].burst_mount);
        assert(alloc_test <= qos_record_for_ul[SERVICE_rtPS].allocated);

        for (unsigned int i = 0; i < rtPSList.size() && ulduration < maxulduration; i++) {
            if (rtPSList[i]->getUlDuration() != 0) {
                ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
                ub->setCid(rtPSList[i]->get_cid());
                ub->setIUC(map->getUlSubframe()->getProfile(rtPSList[i]->getPeerNode()->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getIUC());
                ub->setStarttime(ulduration);
                if (rtPSList[i]->getUlDuration() <= maxulduration - ulduration) {
                    ub->setDuration(rtPSList[i]->getUlDuration());
                    ulduration += ub->getDuration();
                } else {
                    ub->setDuration(maxulduration - ulduration);
                    ulduration = maxulduration;
                }
            }
        }
    }
    for (unsigned i = 0; i < rtPSList.size() && ulduration < maxulduration ; i++) {
        if (((++rtPSList[i]->last_req_grant_) % rtPSList[i]->get_serviceflow()->getQoS()->getRequestInterval()) == 0) {
            ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
            ub->setCid(rtPSList[i]->get_cid());
            ub->setIUC(UIUC_REQ_REGION_FOCUSED); //UIUC for unicast polling
            ub->setStarttime(ulduration);
            txtime_s = 1;
            ub->setDuration(txtime_s);
            ulduration += ub->getDuration();

            //printf("%d alloc\n",i);
            //qos_record_for_ul[SERVICE_rtPS].allocated++;
        }
    }


    //}
    //    for (unsigned i = 0; i < rtPSList.size() && ulduration < maxulduration; i++) {
    //        if (rtPSList[i]->getBw() != 0) {
    //            ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
    //            ub->setCid(rtPSList[i]->get_cid());
    //            ub->setIUC(map->getUlSubframe()->getProfile(peer->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getIUC());
    //            ub->setStarttime(ulduration);
    //            txtime = phy->getTrxSymbolTime(rtPSList[i]->getBw(), map->getUlSubframe()->getProfile(ub->getIUC())->getEncoding());
    //            txtime_s = (int) ceil(txtime / phy->getSymbolTime());
    //            if (txtime_s <= maxulduration - ulduration) {
    //                ub->setDuration(txtime_s);
    //                ulduration += ub->getDuration();
    //                rtPSList[i]->setBw(0);
    //            } else {
    //                //fill up the rest
    //                ub->setDuration(maxulduration - ulduration);
    //                ulduration = maxulduration;
    //                rtPSList[i]->setBw(rtPSList[i]->getBw() * ub->getDuration() / txtime_s);
    //            }
    //            rtPSList[i]->setLastGrantTime(NOW);
    //        }
    //    }
    //7.3-If the deadline of some rtPS request is in the next frame, schedule this request.
    //7.4-assign priority values for the requests in the intermetiate queue to decide which
    //    ones will be scheduled. The priority computation considers the min_bw request,
    //    backlogged BW and granted BW in order to provide the min_bw request of each flow
    //7.5-schedule the requests
    //7.6-if there are available slots in the uplink subframe, schedule BE
    qos_record_for_ul[SERVICE_BE].allocated = 0;
    peer = mac_->getPeerNode(nextUL_);
    if (peer == NULL) //if the node is not here, lets start from the beginning
        peer = mac_->getPeerNode_head();
    if (peer) {
        for (int i = 0; i < mac_->getNbPeerNodes() && ulduration < maxulduration && qos_record_for_ul[SERVICE_BE].allocated < qos_record_for_ul[SERVICE_BE].burst_mount/*some slots available*/; i++) {
            //fprintf (stdout,"We have %d macs %f\n", mac_->getNbPeerNodes(),NOW);

            //The following code uses BW information to allocate data
            if (peer->getReqBw(SERVICE_BE) > 0 && !getMac()->isPeerScanning(peer->getAddr())) {
                vector<Connection*> tmpList = peer->getflow(SERVICE_BE);
                for (unsigned int i = 0; i < tmpList.size() && ulduration < maxulduration; i++) {
                    if (tmpList[i]->getBw()) {
                        ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
                        ub->setCid(tmpList[i]->get_cid()); //specific cid(type = CONN_DATA)
                        ub->setIUC(map->getUlSubframe()->getProfile(peer->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getIUC());
                        ub->setStarttime(ulduration);
                        txtime = phy->getTrxSymbolTime(tmpList[i]->getBw(), map->getUlSubframe()->getProfile(ub->getIUC())->getEncoding());
                        txtime_s = (int) ceil(txtime / phy->getSymbolTime() - 0.000001f);
                        if (txtime_s <= maxulduration - ulduration) {
                            ub->setDuration(txtime_s);
                            ulduration += ub->getDuration(); //add 1 OFDM symbol between bursts
                            qos_record_for_ul[SERVICE_rtPS].allocated += txtime_s;
                            tmpList[i]->setBw(0);
                        } else {
                            //fill up the rest
                            ub->setDuration(maxulduration - ulduration);
                            ulduration = maxulduration;
                            tmpList[i]->setBw(tmpList[i]->getBw() * ub->getDuration() / txtime_s);
                        }
                        //printf("duration= %d\n", ub->getDuration());
                    }
                }
                //fprintf(stdout,"At %f Allocating UL burst for Node %d\n", NOW, peer->getAddr());
                //add a pdu and burst for this node
                //ub = (UlBurst*) map->getUlSubframe()->addPhyPdu(nbulpdus++, 0)->addBurst(0);
                //ub->setCid(peer->getBasic(IN_CONNECTION)->get_cid());
                //printf("%d ",ub->getCid());
                //get the profile (we do a simple translation so that DL_PROFILE_1 = UL_PROFILE_1
                //printf ("DIUC_1=%d UIUC_1=%d, DIUCreq=%d, UIUC=%d\n", DIUC_PROFILE_1, UIUC_PROFILE_1, peer->getDIUC(), DIUC_PROFILE_1+UIUC_PROFILE_1-peer->getDIUC());
                //ub->setIUC(map->getUlSubframe()->getProfile(peer->getDIUC() - DIUC_PROFILE_1 + UIUC_PROFILE_1)->getIUC());
                //ub->setStarttime(ulduration);
                //txtime = phy->getTrxSymbolTime(peer->getReqBw(SERVICE_BE), map->getUlSubframe()->getProfile(ub->getIUC())->getEncoding());
                //txtime_s = (int) round(txtime / phy->getSymbolTime()); //in units of symbol
                //if (txtime_s <= maxulduration - ulduration) {
                //   ub->setDuration(txtime_s);
                // ulduration += ub->getDuration() + 1; //add 1 OFDM symbol between bursts
                // } else {
                // //fill up the rest
                //     ub->setDuration(maxulduration - ulduration);
                //     ulduration = maxulduration;
                //    }
                // mac_->debug("duration= %d\n", ub->getDuration());
            }

            peer = peer->next_entry();
            if (peer == NULL)
                peer = mac_->getPeerNode_head();

            if (peer)
                nextUL_ = peer->getAddr(); //go to next one
            else
                nextUL_ = -1; //will go the the beginning of the list
        }
    }

    //end of map
    ub = (UlBurst*) mac_->getMap()->getUlSubframe()->addPhyPdu(nbulpdus, 0)->addBurst(0);
    ub->setIUC(UIUC_END_OF_MAP);
    ub->setStarttime(maxulduration);

    assert(mac_->getFrameDuration() > map->getUlSubframe()->getStarttime() * phy->getPS() + maxulduration * phy->getSymbolTime());
    //printf ("Start %f end=%f\n",NOW+map->getUlSubframe()->getStarttime()*phy->getPS(), NOW+map->getUlSubframe()->getStarttime()*phy->getPS()+maxulduration*phy->getSymbolTime ());
    //printf ("End2=%f\n", NOW+map->getUlSubframe()->getStarttime()*phy->getPS()+ub->getStarttime()*mac_->getPhy()->getSymbolTime());

    //now transfert the packets to the bursts starting with broadcast messages
    packgeBroadcastMsg();
    //@todo: printUL_MAP() is here
    //map->printUL_MAP();
}

/**
 * get all the connection with the given qos type
 * @param qos type
 * @return list of connection pointer
 */
vector<Connection*> BSScheduler::getflow(SchedulingType_t type) {
    PeerNode *peer;
    vector<Connection *> flowList, tmpList;

    peer = mac_->getPeerNode_head();
    if (peer != NULL) {
        for (int i = 0; i < mac_->getNbPeerNodes(); i++) {
            tmpList = peer->getflow(type);
            for (unsigned int j = 0; j < tmpList.size(); j++) {
                flowList.push_back(tmpList[j]);
            }
            peer = peer->next_entry();
        }
    }
    return flowList;
}

/**
 * print out the qos record
 */
void BSScheduler::printqos() {
    printf("At %lf, the used/available burst for qos\n", NOW);
    printf("\t\033[m     UGS:\033[1;37m%3.3lf/%-3d\n", qos_record_for_ul[SERVICE_UGS].used, qos_record_for_ul[SERVICE_UGS].burst_mount);
    printf("\t\033[m   ertPS:\033[1;37m%3.3lf/%-3d\n", qos_record_for_ul[SERVICE_ertPS].used, qos_record_for_ul[SERVICE_ertPS].burst_mount);
    printf("\t\033[m    rtPS:\033[1;37m%3.3lf/%-3d\n", qos_record_for_ul[SERVICE_rtPS].used, qos_record_for_ul[SERVICE_rtPS].burst_mount);
    printf("\t\033[m   nrtPS:\033[1;37m%3.3lf/%-3d\n", qos_record_for_ul[SERVICE_nrtPS].used, qos_record_for_ul[SERVICE_nrtPS].burst_mount);
    printf("\t\033[m      BE:\033[1;37m%3.3lf/%-3d\033[m\n", qos_record_for_ul[SERVICE_BE].used, qos_record_for_ul[SERVICE_BE].burst_mount);
}

/**
 * allocate DLMAP
 * @param max download duration
 */
void BSScheduler::alloc_dlmap(int maxdlduration) {
    Packet *p;
    struct hdr_cmn *ch;
    double txtime; //tx time for some data (in second)
    int txtime_s; //number of symbols used to transmit the data
    DlBurst *db;
    PeerNode *peer;

    OFDMPhy *phy = mac_->getPhy();
    FrameMap *map = mac_->getMap();

    int dlduration = DL_PREAMBLE; //number of symbols currently used for downlink
    int nbdlbursts = 0;

    //1-First burst for broadcast messages are using DIUC_PROFILE_4
    db = (DlBurst*) map->getDlSubframe()->getPdu()->addBurst(nbdlbursts++);
    db->setCid(BROADCAST_CID);
    db->setIUC(map->getDlSubframe()->getProfile(DIUC_PROFILE_4)->getIUC());
    db->setStarttime(dlduration); //after preamble and fch
    db->setPreamble(true); //this is the first burst after preamble

    //time to send DL_MAP and UL_MAP
    txtime = phy->getTrxSymbolTime(2 * sizeof (struct gen_mac_header_t) + sizeof (struct mac802_16_dl_map_frame) + sizeof (struct mac802_16_ul_map_frame), map->getDlSubframe()->getProfile(db->getIUC())->getEncoding());
    txtime_s = (int) round(txtime / phy->getSymbolTime()); //in units of symbol
    if ((dlduration + txtime_s) > maxdlduration) {
        fprintf(stderr, "Error: Not enough symbols on the downlink subframe to send synchronization messages\n");
        fprintf(stderr, "       maxdlduration=%d, needed=%d\n", maxdlduration, dlduration + txtime_s);
        fprintf(stderr, "       Consider incrementing the dlratio_ or using a better modulation\n");
        exit(0);
    }

    db->setDuration(txtime_s); //enough to send DL_MAP...

    //check if we have to add DCD and/or UCD to transmit and increase burst size if
    //necessary
    if (getMac()->sendDCD || map->getDlSubframe()->getCCC() != getMac()->dlccc_) {
        p = map->getDCD();
        ch = HDR_CMN(p);
        txtime = phy->getTrxTime(ch->size(), map->getDlSubframe()->getProfile(db->getIUC())->getEncoding());
        ch->txtime() = txtime;
        txtime_s = (int) ceil(txtime / phy->getSymbolTime()); //in units of symbol
        assert((dlduration + db->getDuration() + txtime_s) <= maxdlduration); //if this occurs, increase dlratio
        db->setDuration(db->getDuration() + txtime_s);
    }
    if (getMac()->sendUCD || map->getUlSubframe()->getCCC() != getMac()->ulccc_) {
        p = map->getUCD();
        ch = HDR_CMN(p);
        txtime = phy->getTrxTime(ch->size(), map->getDlSubframe()->getProfile(db->getIUC())->getEncoding());
        ch->txtime() = txtime;
        txtime_s = (int) ceil(txtime / phy->getSymbolTime()); //in units of symbol
        assert((dlduration + db->getDuration() + txtime_s) <= maxdlduration); //if this occurs, increase dlratio
        db->setDuration(db->getDuration() + txtime_s);
    }

    //check if we have additional packets in the broadcast queue
    if (mac_->getCManager()->get_connection(BROADCAST_CID, OUT_CONNECTION)->queueByteLength() > 0) {
        //printf ("Additional %d packet(s) in Broadcast\n", mac_->getCManager()->get_connection (BROADCAST_CID, OUT_CONNECTION)->queueLength());
        //other packets to send, increase the broadcast duration
        txtime = phy->getTrxSymbolTime(mac_->getCManager()->get_connection(BROADCAST_CID, OUT_CONNECTION)->queueByteLength(), map->getDlSubframe()->getProfile(db->getIUC())->getEncoding());
        txtime_s = (int) round(txtime / phy->getSymbolTime()); //in units of symbol. We add one to create a small gap between bursts
        if (txtime_s <= maxdlduration - dlduration - db->getDuration()) {
            db->setDuration(db->getDuration() + txtime_s);
        } else {
            //fill up the rest
            db->setDuration(maxdlduration - dlduration);
        }
    }
    dlduration += db->getDuration() + 1; //add 1 OFDM symbol between padding (to avoid collisions)

    /*We don't have to modify DOWNLINK SCHEDULE (yi_hong)*/
    //First implementation, return the CIDs in a RR fashion
    peer = mac_->getPeerNode(nextDL_);
    if (peer == NULL) //if the node is not here, lets start from the beginning
        peer = mac_->getPeerNode_head();
    if (peer) {
        for (int i = 0; i < mac_->getNbPeerNodes() && dlduration < maxdlduration; i++) {
            //for each node, the order is as follows:
            //basic, primary, secondary, data
            if (peer->getBasic(OUT_CONNECTION) && peer->getBasic(OUT_CONNECTION)->queueByteLength() > 0) {
                dlduration = addDlBurst(nbdlbursts++, peer->getBasic(OUT_CONNECTION), peer->getDIUC(), dlduration, maxdlduration);
            }
            if (peer->getPrimary(OUT_CONNECTION) && peer->getPrimary(OUT_CONNECTION)->queueByteLength() > 0 && dlduration < maxdlduration) {
                dlduration = addDlBurst(nbdlbursts++, peer->getPrimary(OUT_CONNECTION), peer->getDIUC(), dlduration, maxdlduration);
            }
            if (peer->getSecondary(OUT_CONNECTION) && peer->getSecondary(OUT_CONNECTION)->queueByteLength() > 0 && dlduration < maxdlduration) {
                dlduration = addDlBurst(nbdlbursts++, peer->getSecondary(OUT_CONNECTION), peer->getDIUC(), dlduration, maxdlduration);
            }
            assert(peer->getOutDataList()->size() <= 1);
            if (!peer->getOutDataList()->empty() && peer->getOutDataList()->at(0)->queueByteLength() > 0 && dlduration < maxdlduration) {
                dlduration = addDlBurst(nbdlbursts++, peer->getOutDataList()->at(0), peer->getDIUC(), dlduration, maxdlduration);
            }
            peer = peer->next_entry();
            if (peer == NULL)
                peer = mac_->getPeerNode_head();

            if (peer)
                nextDL_ = peer->getAddr(); //go to next one
            else
                nextDL_ = -1; //will go the the beginning of the list
        }
    }
    //fprintf(stderr,"%d\n%d\n",dlduration,maxdlduration);
    assert(dlduration <= maxdlduration);

    //3-Add the End of map element
    db = (DlBurst*) map->getDlSubframe()->getPdu()->addBurst(nbdlbursts); //no++, last burst
    db->setIUC(DIUC_END_OF_MAP);
    db->setStarttime(maxdlduration);
}

/**
 * package broadcast message
 */
void BSScheduler::packgeBroadcastMsg() {
    //now transfert the packets to the bursts starting with broadcast messages
    Packet *p;
    OFDMPhy *phy = mac_->getPhy();
    FrameMap *map = mac_->getMap();
    Burst *b = map->getDlSubframe()->getPdu()->getBurst(0);
    struct hdr_cmn *ch;
    double txtime; //tx time for some data (in second)
    int b_data = 0;
    int max_data = phy->getMaxPktSize(b->getDuration(), mac_->getMap()->getDlSubframe()->getProfile(b->getIUC())->getEncoding()) - b_data;

    p = map->getDL_MAP();
    ch = HDR_CMN(p);
    txtime = phy->getTrxTime(ch->size(), map->getDlSubframe()->getProfile(b->getIUC())->getEncoding());
    ch->txtime() = txtime;
    assert(b_data + ch->size() <= max_data);
    ch->timestamp() = NOW; //add timestamp since it bypasses the queue
    b->enqueue(p); //enqueue into burst
    b_data += ch->size();

    //map->printUL_MAP();
    p = map->getUL_MAP();
    ch = HDR_CMN(p);
    txtime = phy->getTrxTime(ch->size(), map->getDlSubframe()->getProfile(b->getIUC())->getEncoding());
    ch->txtime() = txtime;
    assert(b_data + ch->size() <= max_data);
    ch->timestamp() = NOW; //add timestamp since it bypasses the queue
    b->enqueue(p); //enqueue into burst
    b_data += ch->size();

    if (getMac()->sendDCD || map->getDlSubframe()->getCCC() != getMac()->dlccc_) {
        p = map->getDCD();
        ch = HDR_CMN(p);
        txtime = phy->getTrxTime(ch->size(), map->getDlSubframe()->getProfile(b->getIUC())->getEncoding());
        ch->txtime() = txtime;
        assert(b_data + ch->size() <= max_data);
        ch->timestamp() = NOW; //add timestamp since it bypasses the queue
        b->enqueue(p); //enqueue into burst
        b_data += ch->size();
    }

    if (getMac()->sendUCD || map->getUlSubframe()->getCCC() != getMac()->ulccc_) {
        p = map->getUCD();
        ch = HDR_CMN(p);
        txtime = phy->getTrxTime(ch->size(), map->getDlSubframe()->getProfile(b->getIUC())->getEncoding());
        ch->txtime() = txtime;
        assert(b_data + ch->size() <= max_data);
        ch->timestamp() = NOW; //add timestamp since it bypasses the queue
        b->enqueue(p); //enqueue into burst
        b_data += ch->size();
    }

    //Get other broadcast messages
    Connection *c = mac_->getCManager()->get_connection(b->getCid(), OUT_CONNECTION);
    b_data = transfer_packets(c, b, b_data);

    //Now get the other bursts
    for (int index = 1; index < map->getDlSubframe()->getPdu()->getNbBurst() - 1; index++) {
        Burst *b = map->getDlSubframe()->getPdu()->getBurst(index);
        int b_data = 0;

        Connection *c = mac_->getCManager()->get_connection(b->getCid(), OUT_CONNECTION);
        assert(c);
        //printf ("Transferring data to burst %d\n", b->getCid());
        b_data = transfer_packets(c, b, b_data);
    }
}

/**
 * Add a downlink burst with the given information
 * @param burstid The burst number
 * @param c The connection to add
 * @param iuc The profile to use
 * @param dlduration current allocation status
 * @param the new allocation status
 */
int BSScheduler::addDlBurst(int burstid, Connection *c, int iuc, int dlduration, int maxdlduration) {
    double txtime; //tx time for some data (in second)
    int txtime_s; //number of symbols used to transmit the data
    OFDMPhy *phy = mac_->getPhy();

    //add a burst for this node
    DlBurst *db = (DlBurst*) mac_->getMap()->getDlSubframe()->getPdu()->addBurst(burstid);
    db->setCid(c->get_cid());
    db->setIUC(iuc);
    db->setStarttime(dlduration);

    txtime = phy->getTrxSymbolTime(c->queueByteLength(), mac_->getMap()->getDlSubframe()->getProfile(db->getIUC())->getEncoding());
    txtime += c->queueLength() * TX_GAP; //add small gaps between packets to send
    txtime_s = (int) ceil(txtime / phy->getSymbolTime()); //in units of symbol
    if (txtime_s < maxdlduration - dlduration) {
        db->setDuration(txtime_s);
        dlduration += db->getDuration() + 1; //add 1 OFDM symbol between bursts
    } else {
        //fill up the rest
        db->setDuration(maxdlduration - dlduration);
        dlduration = maxdlduration;
    }
    return dlduration;
}
